﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;

namespace core32_asm
{
    class Program
    {
        static FileStream output;

        static string com = "";
        static string op0 = "";
        static string op1 = "";
        static string op2 = "";
        static string remain = "";

        static bool isnumber;

        static string[] lines = new string[2048];
        static int line = 0;

        static byte[,] code_buffer = new byte[4,65536];
        static int bytecount=0;
        static int code_buffer_anz = 0;

        static ArrayList labels = new ArrayList();


        static void Main(string[] args)
        {
            // file stuff
            string filename="test.txtasm";
            if (args.Length>0)
                filename = args[0];

            Console.WriteLine(filename);

            StreamReader file = new StreamReader(filename);

            string path = "";

            while (filename.Contains("\\"))
            {
                path += filename.Substring(0, filename.IndexOf("\\")+1);
                filename = filename.Substring(filename.IndexOf("\\")+1, filename.Length - filename.IndexOf("\\") -1);
            }

            output = new FileStream(path+"output.hex", FileMode.Create);

            while (file.EndOfStream == false)
            {
                lines[line] = file.ReadLine();
                line++;
            }
            // end file stuff

            parse_file();

            Console.WriteLine("######################");

            for (int i = 0; i < code_buffer_anz; i++)
            {
                Console.WriteLine(code_buffer[0, i] + " | " + code_buffer[1, i] + " | " + code_buffer[2, i] + " | " + code_buffer[3, i] + " | ");
            }

            shorten_code();

            redirect_jumps();

            Console.WriteLine("######################");

            for (int i = 0; i < code_buffer_anz; i++)
            {
                Console.WriteLine(code_buffer[0, i] + " | " + code_buffer[1, i] + " | " + code_buffer[2, i] + " | " + code_buffer[3, i] + " | ");
            }

            Console.WriteLine("######################");

            write_buffer();

            file.Close();
            output.Close();

            Console.ReadLine();

        }


        static void parse_file()
        {
            for (int i = 0; i < line; i++)
            {
                Console.WriteLine(lines[i]);

                if (lines[i].IndexOf(" ") == -1)
                    com = lines[i];
                else
                {
                    com = lines[i].Substring(0, lines[i].IndexOf(" "));
                    remain = lines[i].Substring(lines[i].IndexOf(" ") + 1, lines[i].Length - lines[i].IndexOf(" ") - 1);

                    if (remain.IndexOf(",") == -1)
                        op0 = remain;
                    else
                    {
                        op0 = remain.Substring(0, remain.IndexOf(","));
                        remain = remain.Substring(remain.IndexOf(",") + 1, remain.Length - remain.IndexOf(",") - 1);

                        if (remain.IndexOf(",") == -1)
                            op1 = remain;
                        else
                        {
                            op1 = remain.Substring(0, remain.IndexOf(","));
                            op2 = remain.Substring(remain.IndexOf(",") + 1, remain.Length - remain.IndexOf(",") - 1);

                        }

                    }
                }

                if (op1[0] == '#' || op2[0] == '#')
                    isnumber = true;
                else
                    isnumber = false;

                if (com == "li")
                    li();

                if (com == "add")
                    add();

                if (com == "sub")
                    sub();

                if (com == "store")
                    store();

                if (com == "nop")
                    nop();

                if (com == "stop")
                    stop();

                if (com == "jnz")
                    jnz();

                if (com == ":")
                    label();

                if (com == "swap")
                    swap();

                if (com == "sll")
                    sll();

                if (com == "srl")
                    srl();

                if (com == "and")
                    and();

                if (com == "or")
                    or();

                if (com == "xor")
                   xor();

                if (com == "mul")
                    mul();

            }
        }


        static void fill_buffer(byte incoming)
        {
            if (bytecount == 4)
            {
                code_buffer_anz++;
                bytecount = 0;
            }

            code_buffer[bytecount, code_buffer_anz] = incoming;

            bytecount++;
        }


        static void write_buffer()
        {
            for (int i = 0; i < code_buffer_anz; i++)
            {
                output.WriteByte(code_buffer[0, i]);
                output.WriteByte(code_buffer[1, i]);
                output.WriteByte(code_buffer[2, i]);
                output.WriteByte(code_buffer[3, i]);
            }
        }


        static void shorten_code()
        {
            bool restart = false;

            int[] busy = new int[9];
            for (int i = 0; i < 9; i++)
                busy[i] = 0;

            bool[] blocked = new bool[9];
            for (int i = 0; i < 9; i++)
                blocked[i] = false;

            int reg0, reg1, reg2;

            for (int i = 0; i < code_buffer_anz; i++)
            {
                //Console.WriteLine(code_buffer[0, i] + " | " + code_buffer[1, i] + " | " + code_buffer[2, i] + " | " + code_buffer[3, i] + " | ");
                //Console.WriteLine("#############");

                reg0 = (code_buffer[1, i] & 112)>>4;
                reg1 = 8;
                if (Convert.ToBoolean((code_buffer[1, i] & 8) >> 3) == false) reg1 = (code_buffer[1, i] & 7);
                reg2 = 8;
                if (Convert.ToBoolean((code_buffer[1, i] & 128) >> 7) == false) reg2 = ((code_buffer[2, i] & 112)>>4);

                //Console.WriteLine("reg 0 : " + reg0 + " | Busy : " + busy[reg0]);
                //Console.WriteLine("reg 1 : " + reg1);
                //Console.WriteLine("reg 2 : " + reg2);
                //Console.ReadLine();

                if (code_buffer[0, i] > 0x40 && (code_buffer[0, i] < 0x80))
                {
                    busy[reg0] = 3;
                }

                if (code_buffer[0, i] == 0x40)
                {
                    for (int j = i + 1; j < code_buffer_anz; j++)
                    {
                        if (code_buffer[0, j] > 0x40 && (code_buffer[0, j] < 0x80))
                        {
                            reg0 = (code_buffer[1, j] & 112) >> 4;
                            reg1 = 8;
                            if (Convert.ToBoolean((code_buffer[1, j] & 8) >> 3) == false) reg1 = (code_buffer[1, j] & 7);
                            reg2 = 8;
                            if (Convert.ToBoolean((code_buffer[1, j] & 128) >> 7) == false) reg2 = ((code_buffer[2, j] & 112) >> 4);

                            if ((busy[reg1] < 1 || reg1 == 8) && (busy[reg2] < 1 || reg2 == 8) && blocked[reg0] == false)
                            {
                                //Console.WriteLine("Hit");
                                //Console.WriteLine(code_buffer[0, j] + " | " + code_buffer[1, j] + " | " + code_buffer[2, j] + " | " + code_buffer[3, j]);
                                //Console.WriteLine(reg0 + ":" + blocked[reg0] + " | " + reg1 + ":" + busy[reg1] + "|" + reg2 + ":" + busy[reg2]);
                                code_buffer[0, i] = code_buffer[0, j];
                                code_buffer[1, i] = code_buffer[1, j];
                                code_buffer[2, i] = code_buffer[2, j];
                                code_buffer[3, i] = code_buffer[3, j];
                                code_buffer[0, j] = 0x40;
                                code_buffer[1, j] = 0x00;
                                code_buffer[2, j] = 0x00;
                                code_buffer[3, j] = 0x00;
                                restart = true;
                                break;
                            }
                            else
                            {
                                //Console.WriteLine(code_buffer[0, j] + " | " + code_buffer[1, j] + " | " + code_buffer[2, j] + " | " + code_buffer[3, j]);
                                //Console.WriteLine(reg0 + ":" + blocked[reg0] + " | " + reg1 + ":" + busy[reg1] + "|" + reg2 + ":" + busy[reg2]);
                                blocked[reg1] = true;
                                blocked[reg2] = true;
                            }
                        }
                        else
                        {
                            if (code_buffer[0, j] >= 0x80)
                            {
                                remove_nops();
                                break;
                            }
                        }
                    }
                }

                for (int j = 0; j < 8; j++)
                    busy[j]--;

                if (restart == true)
                    i = code_buffer_anz;
            }

            //Console.WriteLine("---END---");

            if (restart == true)
                shorten_code();

        }


        static void remove_nops()
        {
            int nops=0;

            for (int i = 0; i < code_buffer_anz; i++)
            {
                if (code_buffer[0, i] == 0x40)
                    nops++;
                else
                    nops = 0;

                //Console.WriteLine(code_buffer[0, i] + " | " + nops);
                //Console.ReadLine();

                if (nops > 2)
                {
                    for (int j = i+1; j < code_buffer_anz; j++)
                    {
                        code_buffer[0, j - 1] = code_buffer[0, j];
                        code_buffer[1, j - 1] = code_buffer[1, j];
                        code_buffer[2, j - 1] = code_buffer[2, j];
                        code_buffer[3, j - 1] = code_buffer[3, j];
                    }

                    for (int a = 0; a < code_buffer_anz; a++)
                    {
                        //Console.WriteLine(code_buffer[0, a] + " | " + code_buffer[1, a]);
                    }
                    //Console.ReadLine();

                    nops=0;
                    i=0;
                    code_buffer_anz--;
                }
            }
        }


        static void redirect_jumps()
        {
            UInt32 addr,saddr;
            for (int i = 0; i < code_buffer_anz; i++)
            {
                if (code_buffer[0, i] == 0x80)
                {
                    addr=Convert.ToUInt32((code_buffer[1, i]<<16)+(code_buffer[2, i]<<8)+(code_buffer[3, i]));
                    for (int j = 0; j < code_buffer_anz; j++)
                    {
                        if (code_buffer[0,j]==0xFF)
                        {
                            saddr=Convert.ToUInt32((code_buffer[1, j]<<16)+(code_buffer[2, j]<<8)+(code_buffer[3, j]));
                            if (addr == saddr)
                            {
                                code_buffer[1, i] = (byte)(j >> 16);
                                code_buffer[2, i] = ((byte)((j & 65280) >> 8));
                                code_buffer[3, i] = ((byte)(j & 255));
                                code_buffer[0, j] = (byte)(0x40);
                                code_buffer[1, j] = (byte)(0x00);
                                code_buffer[2, j] = (byte)(0x00);
                                code_buffer[3, j] = (byte)(0x00);
                                remove_nops();
                                break;
                            }
                        }
                    }
                }
            }
        }


        static void label()
        {
            labels.Add(op0);
            fill_buffer(0xFF);
            fill_buffer((byte)((labels.Count-1)>>16));
            fill_buffer((byte)(((labels.Count - 1) & 65280) >> 8));
            fill_buffer((byte)((labels.Count - 1) & 255));
        }


        static void li()
        {
            UInt32 lower,higher;

            lower = Convert.ToUInt32(op1.Substring(1, op1.Length - 1));

            if (lower < 65536)
            {
                lower = Convert.ToUInt32(lower & 65535);
                fill_buffer(0x42);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 136));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                higher = Convert.ToUInt32(lower & 4294901760);
                higher = higher >> 16;
                lower = Convert.ToUInt32(lower & 65535);

                fill_buffer(0x42);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 136));
                fill_buffer((byte)((higher & 65280) >> 8));
                fill_buffer((byte)(higher & 255));

                nop();
                nop();

                op1 = op0;
                swap();

                nop();
                nop();

                fill_buffer(0x42);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op0)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            nop();
            nop();

        }

        static void add()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x42);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x42);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void sub()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x43);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x43);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void store()
        {
            fill_buffer(0xC0);
            fill_buffer(Convert.ToByte(Convert.ToByte(op0)));
            fill_buffer(Convert.ToByte(Convert.ToByte(op1) << 4));
            fill_buffer(Convert.ToByte(0));

            nop();
        }

        static void nop()
        {
            fill_buffer(0x40);
            fill_buffer(0x00);
            fill_buffer(0x00);
            fill_buffer(0x00);
        }

        static void stop()
        {
            fill_buffer(0x00);
            fill_buffer(0x00);
            fill_buffer(0x00);
            fill_buffer(0x00);

            nop();
            nop();
        }

        static void jnz()
        {
            fill_buffer(0x80);

            int i;
            for (i = 0; i < labels.Count; i++)
                if (labels[i] == op0)
                    break;

            fill_buffer((byte)((i - 1) >> 16));
            fill_buffer((byte)(((i - 1) & 65280) >> 8));
            fill_buffer((byte)((i - 1) & 255));
            
            nop();
        }

        static void swap()
        {
            fill_buffer(0x44);
            fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
            fill_buffer(Convert.ToByte(0));
            fill_buffer(Convert.ToByte(0));

        }

        static void sll()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                lower = lower % 32;
                fill_buffer(0x45);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x45);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void srl()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                lower = lower % 32;
                fill_buffer(0x45);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                lower += 32;
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x45);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(32));
            }
            nop();
            nop();
        }

        static void and()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x47);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x47);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void or()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x48);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x48);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void xor()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x49);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x49);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }

        static void mul()
        {
            if (isnumber == true)
            {
                UInt32 lower = Convert.ToUInt32(op2.Substring(1, op2.Length - 1));
                fill_buffer(0x4B);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + 128 + Convert.ToByte(op1)));
                fill_buffer((byte)((lower & 65280) >> 8));
                fill_buffer((byte)(lower & 255));
            }
            else
            {
                fill_buffer(0x4B);
                fill_buffer(Convert.ToByte((Convert.ToByte(op0) << 4) + Convert.ToByte(op1)));
                fill_buffer(Convert.ToByte(Convert.ToByte(op2) << 4));
                fill_buffer(Convert.ToByte(0));
            }
            nop();
            nop();
        }



    }
}
